package com.lancoo.rxretrofitlibrary.http;

import android.text.TextUtils;
import android.util.Log;

import com.lancoo.rxretrofitlibrary.exception.InvalidUrlException;
import com.lancoo.rxretrofitlibrary.http.parser.DefaultUrlParser;
import com.lancoo.rxretrofitlibrary.http.parser.UrlParser;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by tangminglong on 17/8/4.
 */

public class RetrofitUrlManager {
    /**
     * 智能备考系统的基础地址
     */
    public static final String API_DOMAIN_NAME = "apiUrl";
    /**
     * 资源的基础地址
     */
    public static final String RESOURCE_DOMAIN_NAME = "resUrl";
    /**
     * 服务器基础地址
     */
    public static final String SERVER_ADDRESS_NAME = "serverAddressUrl";

    private static final String TAG = "RetrofitUrlManager";
    private static final String DOMAIN_NAME = "Domain-Name";
    public static final String DOMAIN_NAME_HEADER = DOMAIN_NAME + ": ";
    private static final String GLOBAL_DOMAIN_NAME = "tml.globalDomainName";
    private final Map<String, HttpUrl> mDomainNameHub = new HashMap<>();
    private static  boolean DEPENDENCY_OKHTTP = false;
    private DefaultUrlParser mUrlParser;
    private final Interceptor mInterceptor;
    private boolean isRun = true; //默认开始运行,可以随时停止运行,比如你在 App 启动后已经不需要在动态切换 baseurl 了


    static {
        boolean hasDependency;
        try {
            Class.forName("okhttp3.OkHttpClient");
            hasDependency = true;
        } catch (ClassNotFoundException e) {
            hasDependency = false;
        }
        DEPENDENCY_OKHTTP = hasDependency;
    }


    private RetrofitUrlManager() {
        if (!DEPENDENCY_OKHTTP) {
            throw new IllegalStateException("Must be dependency OkHttp");
        }
        setUrlParser(new DefaultUrlParser());
        this.mInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                if (!isRun){
                    return chain.proceed(chain.request());
                }
                return chain.proceed(processRequest(chain.request()));
            }
        };

    }






    /**
     * 将 {@link okhttp3.OkHttpClient.Builder} 传入,配置一些本管理器需要的参数
     *
     * @param builder
     * @return
     */
    public OkHttpClient.Builder with(OkHttpClient.Builder builder){
        return builder
                .addInterceptor(mInterceptor);
    }

    /**
     * 对{@link Request} 进行一些必要的加工
     * @param request
     * @return
     */
    private Request processRequest(Request request) {
        Request.Builder newBuilder = request.newBuilder();
        String domainName = obtainDomainNameFromHeaders(request);
        HttpUrl httpUrl;
        if (!TextUtils.isEmpty(domainName)){
            httpUrl = fetchDomain(domainName);
            newBuilder.removeHeader(DOMAIN_NAME);
        }else {
            httpUrl =fetchDomain(GLOBAL_DOMAIN_NAME);
        }

        if (null != httpUrl){
            HttpUrl newUrl = mUrlParser.parseUrl(httpUrl,request.url());
            return newBuilder
                    .url(newUrl)
                    .build();
        }
        return newBuilder.build();
    }



    /**
     * 从{@link Request#header(String)} 中取出DomainName
     * @param request
     * @return
     */
    private String obtainDomainNameFromHeaders(Request request) {
        List<String> heads = request.headers(DOMAIN_NAME);
        if (heads == null || heads.size() == 0)
            return null;
        if (heads.size() > 1)
            throw new IllegalArgumentException("Only one Domain-Name in the headers");

        return request.header(DOMAIN_NAME);
    }

    /**
     * 可自行实现{@link UrlParser} 动态切换Url 解析策略
     * @param mUrlParser
     */
    public void setUrlParser(DefaultUrlParser mUrlParser) {
        this.mUrlParser = mUrlParser;
    }

    private static class RetrofitUrlManagerHolder{
        private static final RetrofitUrlManager INSTANCE = new RetrofitUrlManager();
    }

    public static RetrofitUrlManager getInstance(){
        return RetrofitUrlManagerHolder.INSTANCE;
    }

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    /**
     * 全局动态替换 BaseUrl，优先级： Header中配置的url > 全局配置的url
     * 除了作为备用的 BaseUrl ,当你项目中只有一个 BaseUrl ,但需要动态改变
     * 这种方式不用在每个接口方法上加 Header,也是个很好的选择
     *
     * @param url
     */
    public void setGlobalDomain(String url) {
        synchronized (mDomainNameHub) {
            mDomainNameHub.put(GLOBAL_DOMAIN_NAME, checkUrl(url));
        }
    }

    /**
     * 获取全局 BaseUrl
     */
    public HttpUrl getGlobalDomain() {
        return mDomainNameHub.get(GLOBAL_DOMAIN_NAME);
    }

    /**
     * 移除全局 BaseUrl
     */
    public void removeGlobalDomain() {
        synchronized (mDomainNameHub) {
            mDomainNameHub.remove(GLOBAL_DOMAIN_NAME);
        }
    }



    /**
     * 存放 Domain 的映射关系
     *
     * @param domainName
     * @param domainUrl
     */
    public void putDomain(String domainName, String domainUrl) {
        synchronized (mDomainNameHub) {
            mDomainNameHub.put(domainName, checkUrl(domainUrl));
        }
    }

    /**
     * 取出对应DomainName 的Url
     * @param domainName
     * @return
     */
    public HttpUrl fetchDomain(String domainName) {
        return mDomainNameHub.get(domainName);
    }

    /**
     * 取出对应DomainName 的Url
     * @param domainName
     * @return
     */
    public String fetchDomainStr(String domainName) {
        return mDomainNameHub.get(domainName).url().toString();
    }

    /**
     * 检查url是否有效
     * @param url
     * @return
     */
    private HttpUrl checkUrl(String url) {
        HttpUrl parseUrl = HttpUrl.parse(url);
        if (null == parseUrl) {
            throw new InvalidUrlException(url);
        } else {
            return parseUrl;
        }
    }


    public void removeDomain(String domainName) {
        synchronized (mDomainNameHub) {
            mDomainNameHub.remove(domainName);
        }
    }

    public void clearAllDomain() {
        mDomainNameHub.clear();
    }

    public boolean haveDomain(String domainName) {
        return mDomainNameHub.containsKey(domainName);
    }

    public int domainSize() {
        return mDomainNameHub.size();
    }
}
